import java.util.ArrayList;
import java.util.concurrent.locks.ReentrantLock;

public class EventManager
{
	private IOProgram IOProgram = new IOProgram();
	private LoadSaveManager LoadSaveManager = new LoadSaveManager();
	private ObjectStorage ObjectStorage = new ObjectStorage();
	private Settings Settings = new Settings();

	private ReentrantLock TryProcessUserMouseActionAntiJamLock = new ReentrantLock();

	private ArrayList<DrawObjectEventProcessor> DrawObjectEventProcessors = null;

	private boolean ChangesExisting(MainWindow.GUIElements GUIElements, LoadSaveDataEx LoadSaveDataEx, LoadSaveManager LoadSaveManager)
	{
		String File1Path = LoadSaveManager.GetOverridePath();

		if (File1Path == null || (File1Path.length() == 0))
			return false;

		String File2Path = "./NCIDE_file_compare.tmp";

		// do exactly the same way as if the user saved the file:
		@SuppressWarnings("unused")
		LoadSaveDataEx LoadSaveDataExTemp = ObjectStorage.SaveAllToFile(File2Path, new LoadSaveDataEx(true, "", GUIElements.GetIOPanel().GetIOProgramString(), ZoomManager.GetZoomFactor()));

		boolean FileHashCodesEqual = CompareFileHashCodes(File1Path, File2Path);

		return !(FileHashCodesEqual);
	}

	public boolean CheckForChangesExisting(MainWindow.GUIElements GUIElements, KeyEventProcessor KeyEventProcessor, MouseEventProcessor MouseEventProcessor)
	{
		BeforeProcessingUserAction(GUIElements, KeyEventProcessor, MouseEventProcessor);

		GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("PLEASE WAIT", false, true, true, true);

		boolean ChangesExisting = ChangesExisting(GUIElements, new LoadSaveDataEx(true, "", GUIElements.GetIOPanel().GetIOProgramString(), ZoomManager.GetZoomFactor()), LoadSaveManager);

		GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoText("PLEASE WAIT");

		AfterProcessingUserAction(GUIElements, KeyEventProcessor, MouseEventProcessor);

		return ChangesExisting;
	}

	private boolean CompareFileHashCodes(String File1Path, String File2Path)
	{ // returns true if equal, false if not

		long HashCode1 = LoadSaveManager.GetFileHashCode(File1Path);
		long HashCode2 = LoadSaveManager.GetFileHashCode(File2Path);

		return (HashCode1 == HashCode2);
	}

	public EventManager()
	{
		InitializeDrawObjectEventProcessors();
	}

	public IOProgram GetIOProgram()
	{
		return IOProgram;
	}

	public LoadSaveManager GetLoadSaveManager()
	{
		return LoadSaveManager;
	}

	public ObjectStorage GetObjectStorage()
	{
		return ObjectStorage;
	}

	public Settings GetSettings()
	{
		return Settings;
	}

	private void InitializeDrawObjectEventProcessors()
	{
		DrawObjectEventProcessors = new ArrayList<DrawObjectEventProcessor>();

		DrawObjectEventProcessors.add(new GeneralEventProcessor()); // call this first!
		DrawObjectEventProcessors.add(new SelectionEventProcessor()); // call this second!
		DrawObjectEventProcessors.add(new CommentFieldEventProcessor());
		DrawObjectEventProcessors.add(new FKeyEventProcessor(GetIOProgram(), GetLoadSaveManager(), GetObjectStorage(), GetSettings()));
		DrawObjectEventProcessors.add(new GridEventProcessor());
		DrawObjectEventProcessors.add(new LineEventProcessor());
		DrawObjectEventProcessors.add(new NeuronEventProcessor());
		DrawObjectEventProcessors.add(new NodeEventProcessor());
		DrawObjectEventProcessors.add(new SampleAndHoldEventProcessor());
		DrawObjectEventProcessors.add(new TextFieldEventProcessor());
		DrawObjectEventProcessors.add(new VisualFieldEventProcessor());
		DrawObjectEventProcessors.add(new CADUEventProcessor());
	}

	@SuppressWarnings("unused")
	private boolean NOT(boolean Value)
	{
		return !Value;
	}

	private int BeforeAfterProcessingUserActionCounter = 0;
	
	private void BeforeProcessingUserAction(MainWindow.GUIElements GUIElements, KeyEventProcessor KeyEventProcessor, MouseEventProcessor MouseEventProcessor)
	{
		if (BeforeAfterProcessingUserActionCounter < 0)
			BeforeAfterProcessingUserActionCounter = 0; // just to make sure
		
		if (BeforeAfterProcessingUserActionCounter == 0)
		{
			if (IOProgram.IsExecuted())
			{
				GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("PAUSED", false, true, false, false);
	
				IOProgram.PauseExecution();
			}
	
			// not useful (looks a bit ugly), COPIED and PASTED messages are shown anyway:
			// GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText(PatienceMessage, false, true, true, true); // will show up only if processing input takes a longer time (e.g. moving millions of objects)
			
			GUIElements.GetMainWindow().DisableRedraw();
		}
		BeforeAfterProcessingUserActionCounter ++;
	}

	private void AfterProcessingUserAction(MainWindow.GUIElements GUIElements, KeyEventProcessor KeyEventProcessor, MouseEventProcessor MouseEventProcessor)
	{
		BeforeAfterProcessingUserActionCounter --;
		
		if (BeforeAfterProcessingUserActionCounter < 0)
			BeforeAfterProcessingUserActionCounter = 0; // just to make sure
		
		if (BeforeAfterProcessingUserActionCounter == 0)
		{
			// GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoText(PatienceMessage); // disabled, see above
	
			if (IOProgram.IsExecuted())
			{
				IOProgram.ResumeExecution();
	
				GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoText("PAUSED");
			}
			else
			{
				GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoText("PAUSED");
			}
	
			if (GUIElements.GetMainWindow().IsHelpEnabled())
				GUIElements.GetMainWindow().UpdateHelpText(); // update statistics
	
			// update title bar messages (TitleBarInfo checks (fast) for each call if already existing)...
	
			GUIElements.GetMainWindow().GetTitleBarInfo().RemoveInfoTextContaining("F4 saves as");
			if (LoadSaveManager.GetOverrideFileName() != null)
			{
				GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("F4 saves as " + LoadSaveManager.GetOverrideFileName(), false, false, false, false);
			}
			
			GUIElements.GetMainWindow().EnableRedraw();
		}
	}

	public void TryProcessUserKeyboardAction(MainWindow.GUIElements GUIElements, KeyEventProcessor KeyEventProcessor, MouseEventProcessor MouseEventProcessor)
	{
		BeforeProcessingUserAction(GUIElements, KeyEventProcessor, MouseEventProcessor);
		
		if (KeyEventProcessor.AnyKeyPressed() ||
			KeyEventProcessor.AnyKeyTyped())
		{
			for (int m = 0; m < DrawObjectEventProcessors.size(); m++)
				DrawObjectEventProcessors.get(m).TryProcessUserKeyboardAction(GUIElements, KeyEventProcessor, MouseEventProcessor, ObjectStorage, IOProgram);
		}

		AfterProcessingUserAction(GUIElements, KeyEventProcessor, MouseEventProcessor);
	}

	public void TryProcessUserMouseAction(MainWindow.GUIElements GUIElements, KeyEventProcessor KeyEventProcessor, MouseEventProcessor MouseEventProcessor)
	{
		BeforeProcessingUserAction(GUIElements, KeyEventProcessor, MouseEventProcessor);

		if (TryProcessUserMouseActionAntiJamLock.tryLock()) // works CLEARLY better with this, in tests, there were no problems with "lost" mouse events, instead, the system worked smoother
		{
			for (int m = 0; m < DrawObjectEventProcessors.size(); m++)
				DrawObjectEventProcessors.get(m).TryProcessUserMouseAction(GUIElements, KeyEventProcessor, MouseEventProcessor, ObjectStorage, IOProgram);
			TryProcessUserMouseActionAntiJamLock.unlock();
		}
		
		AfterProcessingUserAction(GUIElements, KeyEventProcessor, MouseEventProcessor);
	}
}
